Coverage Report

Created: 2026-02-05 09:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\scloud-dns\scloud-dns\src\dns\q_name.rs
Line
Count
Source
1
use crate::exceptions::SCloudException;
2
3
/// Parse a DNS QNAME from a DNS message buffer.
4
///
5
/// This function supports:
6
/// - Standard DNS label encoding
7
/// - Name compression using pointers (RFC 1035, section 4.1.4)
8
///
9
/// # Arguments
10
/// * `buf` - Full DNS message buffer
11
/// * `pos` - Offset in the buffer where the QNAME starts
12
///
13
/// # Returns
14
/// * `(String, usize)`
15
///   - Parsed domain name (e.g. "www.example.com")
16
///   - Number of bytes consumed at the original position
17
///
18
/// # Errors
19
/// Returns an error if:
20
/// - The buffer is too short
21
/// - A label length is invalid
22
/// - Compression pointers are malformed
23
///
24
/// # Notes
25
/// - When compression is used, the returned `usize` corresponds
26
///   to the position right after the pointer (not the expanded name).
27
/// - The caller is responsible for passing the correct initial offset
28
///   (e.g. 12 for the first QNAME in a DNS packet).
29
22
pub(crate) fn parse_qname(buf: &[u8], mut pos: usize) -> Result<(String, usize), SCloudException> {
30
22
    let mut labels = Vec::new();
31
22
    let mut _jumped = false;
32
22
    let mut end_pos = pos;
33
34
    loop {
35
59
        if pos >= buf.len() {
36
2
            return Err(SCloudException::SCLOUD_IMPOSSIBLE_PARSE_QNAME_POS_GREATER_THAN_BUF);
37
57
        }
38
39
57
        let len = buf[pos];
40
41
        // Compression 0xC0xx
42
57
        if len & 0xC0 == 0xC0 {
43
3
            if pos + 1 >= buf.len() {
44
1
                return Err(SCloudException::SCLOUD_IMPOSSIBLE_PARSE_QNAME);
45
2
            }
46
47
2
            let offset = (((len as u16 & 0x3F) << 8) | buf[pos + 1] as u16) as usize;
48
49
2
            if !_jumped {
50
2
                end_pos = pos + 2;
51
2
            
}0
52
53
2
            _jumped = true;
54
55
2
            let (name, _) = parse_qname(buf, offset)
?0
;
56
5
            
labels2
.
extend2
(
name.split('.')2
.
map2
(|s| s.to_string()));
57
2
            break;
58
54
        }
59
60
54
        if len == 0 {
61
16
            if !_jumped {
62
16
                end_pos = pos + 1;
63
16
            
}0
64
16
            break;
65
38
        }
66
67
38
        pos += 1;
68
69
38
        if pos + len as usize > buf.len() {
70
1
            return Err(
71
1
                SCloudException::SCLOUD_IMPOSSIBLE_PARSE_QNAME_POS_AND_LEN_GREATER_THAN_BUF,
72
1
            );
73
37
        }
74
75
37
        let label = &buf[pos..pos + len as usize];
76
37
        let s = String::from_utf8(label.to_vec())
77
37
            .map_err(|_| SCloudException::SCLOUD_IMPOSSIBLE_PARSE_QNAME)
?0
;
78
79
37
        labels.push(s);
80
37
        pos += len as usize;
81
    }
82
83
18
    Ok((labels.join("."), end_pos))
84
22
}
85
86
/// Parse a DNS QNAME at a specific offset and return only the name.
87
///
88
/// This is mainly used internally when resolving compression pointers.
89
#[allow(unused)]
90
2
pub(crate) fn parse_qname_at(buf: &[u8], offset: usize) -> Result<String, SCloudException> {
91
2
    let (name, _consumed) = parse_qname(&buf, offset)
?0
;
92
2
    Ok(name)
93
2
}